package kola.network
{
	import flash.events.EventDispatcher;
	import flash.events.IEventDispatcher;
	import flash.events.NetStatusEvent;
	import flash.net.GroupSpecifier;
	import flash.net.NetConnection;
	import flash.net.NetGroup;
	
	import kola.events.PeerNetworkEvent;
	[Event(name="connectionStatusChanged",type="kola.events.PeerNetworkEvent")]
	[Event(name="peerConnectSuccess",type="kola.events.PeerNetworkEvent")]
	[Event(name="peerGroupConnectSuccess",type="kola.events.PeerNetworkEvent")]
	[Event(name="peerGroupMsgReceived",type="kola.events.PeerNetworkEvent")]
	public class PeerNetwork extends EventDispatcher
	{
		
		
		
		/**
		 * 
		 */
		public var netGroup:NetGroup;
		
		//-------------- BEGIN: NetConnection properties ---------------------//
		/**
		 * This holds the server url to connect to.
		 * default is rtmfp: which connects you to a LAN 
		 */
		public var url:String = "rtmfp:";
		
		/**
		 * This holds the value for developer key which is commonly
		 * use if you're dealing with a Cirrus 
		 */
		public var key:String = "";
		

		//--------------  END: NetConnection properties ---------------------//
		
		
		//-------------- BEGIN: NetGroup/GroupSpecifier properties ---------//
		/**
		 * This holds the selected groupName for the group to setup. this
		 * can be overriden so that users can specify a different group
		 */
		public var groupName:String = "kola/lounge";
		
		/**
		 * 
		 */
		public var postingEnabled:Boolean = true;
		
		/**
		 * 
		 */
		public var multicastEnabled:Boolean = true;
		
		/**
		 * 
		 */
		public var peerToPeerDisabled:Boolean = true;
		
		/**
		 * 
		 */
		public var serverChannelEnabled:Boolean = true;
		
		/**
		 * 
		 */
		public var objectReplicationEnabled:Boolean = true;
		
		/**
		 * 
		 */
		public var routingEnabled:Boolean = true;
		
		/**
		 * 
		 */
		public var ipMulticastMemberUpdatesEnabled:Boolean = true;
		
		/**
		 * 
		 */
		public var ipMulticastAddress:String = "225.225.0.1:30303";
	
		//--------------  END: NetGroup/GroupSpecifier properties ---------//
		
		//---------------------
		//	CONSTRUCTOR
		//---------------------
		
		public function PeerNetwork(netConnection:NetConnection = null)
		{
			if(netConnection)
			{
				_netConnection = netConnection;
			}
			else
			{
				
				this.netConnection = new NetConnection();
				this.netConnection.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
			}
		}
		
		private var _netConnection:NetConnection;
		
		/**
		 * This holds the main connection. 
		 */
		public function get netConnection():NetConnection
		{
			return _netConnection;
		}

		/**
		 * @private
		 */
		public function set netConnection(value:NetConnection):void
		{
			if(_netConnection)
			{
				_netConnection.removeEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
			}
			
			_netConnection = value;
			
			if(_netConnection)
				_netConnection.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
		}

		public function connect():void
		{
			if(netConnection.connected) //if this netConnection is already connected we just call setupGroup right away.
				setupGroup();
			else
				netConnection.connect(url, key); // this netConnection is not yet connected, request to connect.
		}
		
		protected function netStatusHandler(event:NetStatusEvent):void
		{
			var peerNetEvent:PeerNetworkEvent;
			var peerNetStatsEvent:PeerNetworkEvent;
			switch(event.info.code)
			{
				//-----------------------
				//		ERROR CODES
				//-----------------------
				case "NetConnection.Call.Failed":
					//The NetConnection.call() method was not able to invoke the server-side method or command.
					break;
				
				case "NetConnection.Connect.AppShutdown":
					//The application has been shut down 
					break;
				
				case "NetConnection.Connect.Failed":
					//The connection attempt failed.
					break;
				
				case "NetConnection.Connect.Rejected":
					/*
					The client does not have permission to connect to the application, 
					or the application name specified during the connection attempt was not found on the server. 
					This information object also has an application property that contains the value returned by 
					application.rejectConnection().
					*/
					break;
				
				case "NetConnection.Proxy.NotResponding":
					//The proxy server is not responding. See the ProxyStream class.
					break;
				
				//----------------------
				//	STATUS CODES
				//----------------------
				
				case "NetConnection.Connect.Success":
					//The connection attempt succeeded. 
					peerNetEvent = new PeerNetworkEvent(PeerNetworkEvent.PEER_CONNECT_SUCCESS);
					dispatchEvent(peerNetEvent);
					
					
					setupGroup();
					break;
				
				case "NetConnection.Connect.Closed":
					//The connection was closed successfully.
					break;
				
				case "NetGroup.Connect.Failed":
					/*
					The creation of or connection to a NetGroup has failed. 
					For example, this code is sent if there is an error in the GroupSpecifier.
					*/
					break;
				
				case "NetGroup.Connect.Success":
					/*
					Successfully connected to the group! 
					*/
					peerNetEvent = new PeerNetworkEvent(PeerNetworkEvent.PEER_GROUP_CONNECT_SUCCESS);
					dispatchEvent(peerNetEvent);
					
					//change status
					_connected = true;
					peerNetStatsEvent = new PeerNetworkEvent(PeerNetworkEvent.PEER_CONNECTION_STATUS_CHANGED);
					dispatchEvent(peerNetStatsEvent);

					break;
				
				case "NetGroup.LocalCoverage.Notify":
					break;
				
				case "NetGroup.MulticastStream.PublishNotify":
					break;
				
				case "NetGroup.MulticastStream.UnpublishNotify":
					break;
				
				case "NetGroup.Neighbor.Connect":
					/*	
					Sent when a neighbor connects to this node. There are two additional properties: 
					e.info.neighbor, a String specifying the group address of the neighbor. 
					e.info.peerID, a String specifying the peerID of the neighbor.
					*/		
					break;
				
				case "NetGroup.Neighbor.Disconnect":
					/*	
					Sent when a neighbor disconnects to this node. There are two additional properties: 
					e.info.neighbor, a String specifying the group address of the neighbor. 
					e.info.peerID, a String specifying the peerID of the neighbor.
					*/		
					break;
				
				case "NetGroup.Posting.Notify":
					/*
					Dispatched when the group receives a message from the NetGroup.post() method.
					There are two additional properties: info.message, an Object containing the message, and 
					info.messageID, a String containing the message's messageID. 
					*/
					
					peerNetEvent = new PeerNetworkEvent(PeerNetworkEvent.PEER_GROUP_MSG_RECEIVED);
					peerNetEvent.data = event.info.message;
					dispatchEvent(peerNetEvent);
					
					break;
				
				case "NetGroup.SendTo.Notify":
					/*
					Dispatched when a message sent directly to this node is received.
					The info.message property is an Object containing the message. 
					The info.from property is a String specifying the groupAddress from which the message was received. 
					The info.fromLocal property is a Boolean value. The value is true if the message was sent by this node 
					(the local node is the node closest to the destination group address), and false if the message was 
					received from a different node. To implement recursive routing, if info.fromLocal is false, 
					call NetGroup.sendToNearest() to resend the message. 
					*/
					break;
				
				case "NetGroup.Replication.Fetch.SendNotify":
					//Sender is about to send an object at a specified index.
					break;	
				
				case "NetGroup.Replication.Fetch.Failed":
					//Sending Failed...
					break;
				
				case "NetGroup.Replication.Fetch.Result":
					//Object received...
					
					//Optionally you can share this object by call addHaveObjects
					break;
				
				case "NetGroup.Replication.Request": // e.info.index, e.info.requestID
					//We have a request for an object... to write we call 
					//netGroup.writeRequestedObject(event.info.requestID,obj[event.info.index])

					break;

				
				default:
					break;
			}
		}
		
		protected function setupGroup():void
		{
			var groupSpec:GroupSpecifier = new GroupSpecifier(groupName);
			groupSpec.postingEnabled = postingEnabled;
			groupSpec.multicastEnabled = multicastEnabled;
			groupSpec.ipMulticastMemberUpdatesEnabled = ipMulticastMemberUpdatesEnabled;
			groupSpec.peerToPeerDisabled = peerToPeerDisabled;
			groupSpec.routingEnabled = routingEnabled;
			groupSpec.serverChannelEnabled = serverChannelEnabled;
			groupSpec.objectReplicationEnabled = objectReplicationEnabled;
			
			groupSpec.addIPMulticastAddress(ipMulticastAddress);

			netGroup = new NetGroup(netConnection, groupSpec.groupspecWithAuthorizations());
			netGroup.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
		}
		
		/**
		 * are we connected? 
		 */
		private var _connected:Boolean = true;
		/**
		 * 
		 * @return Boolean value if connected or not
		 * READ ONLY 
		 */
		[Bindable("connectionStatusChanged")]
		public function get connected():Boolean
		{
			return netConnection ? netConnection.connected : false;
		}
		
		/**
		 *  
		 * @param An object with defintion of it's type and data.
		 * 
		 */
		public function post(value:Object):void
		{
			netGroup.post(value);
		}
	}
}